__author__ = "ncll"
__data__ = "2020/11/25 20:48"

# 故事情节：煎饼果子老师傅在煎饼果子界摸爬滚打几十年，拥有一身精湛的煎饼果子技术，
# 并总结了一套"古法煎饼果子配方"。
# 可是老师傅年迈已久，在嗝屁之前希望把自己的配方传承下去，
# 于是老师傅把配方传给他的徒弟大猫...

# 定义一个master类

# class Master(object):
#     def __init__(self):
#         # 属性
#         self.kongfu = "古法煎饼果子配方"
#
#     # 实例方法
#     def make_cake(self):
#         print("按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#     # 定义Prentice类，继承了 Master，则Prentice是子类，Master是父类。
# class Prentice(Master):
#
#     pass
#
# # 创建子类Prentice的实例对象大猫
# damao = Prentice()
# # 子类对象可以直接使用父类的属性
# print(damao.kongfu)
# # 子类对象可以直接使用父类的方法
# damao.make_cake()

# 大猫掌握了师傅的配方，可以制作古法煎饼果子。但是大猫是个爱学习的好孩子，
# 他希望学到更多的煎饼果子的做法，于是通过百度搜索，找到了一家煎饼果子培训学校。（多继承）

# class Master(object):
#     def __init__(self):
#         # 属性
#         self.kongfu = "古法煎饼果子配方"
#
#     # 实例方法
#     def make_cake(self):
#         print("按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#     def dayandai(self):
#         print("师傅的大烟袋..")
#
#
# class School(object):
#     def __init__(self):
#         self.kongfu = "现代煎饼果子配方"
#
#     def make_cake(self):
#         print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#     def xiaoyandai(self):
#         print("学校的小烟袋..")
#
#
# class Prentice(School, Master):
#     pass
#
# damao = Prentice()
# print(damao.kongfu)  #执行Master的属性
# damao.make_cake()
#
# # 子类的魔法属性__mro__决定了属性和方法的查找顺序
# print(Prentice.__mro__)
#
# damao.dayandai()  # 不重名不受影响
# damao.xiaoyandai()

# 大猫掌握了 师傅的配方 和 学校的配方，通过研究，
# 大猫在两个配方的基础上，创建了一种全新的煎饼果子配方，
# 称之为 "猫氏煎饼果子配方"。（子类重写父类同名属性和方法）

# class Master(object):
#     def __init__(self):
#         self.kongfu = "古法煎饼果子配方"
#
#     def make_cake(self):
#         print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#
# class School(object):
#     def __init__(self):
#         self.kongfu = "现代煎饼果子配方"
#
#     def make_cake(self):
#         print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#
# class Prentice(School, Master):  # 多继承，继承了多个父类
#     def __init__(self):
#         self.kongfu = "猫氏煎饼果子配方"
#
#     def make_cake(self):
#         print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
# # 如果子类和父类的方法名和属性名相同，则默认使用子类的
# # 叫 子类重写父类的同名方法和属性
# damao = Prentice()
# print(damao.kongfu) # 子类和父类有同名属性，则默认使用子类的
# damao.make_cake() # 子类和父类有同名方法，则默认使用子类的

# 大猫的新配方大受欢迎，
# 但是有些顾客希望也能吃到古法配方和 现代配方 的煎饼果子...
# （子类调用父类的同名属性和方法）

# class Master(object):
#     def __init__(self):
#         self.kongfu = "古法煎饼果子配方"  # 实例变量，属性
#
#     def make_cake(self):                    # 实例方法，方法
#         print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#
# class School(object):
#     def __init__(self):
#         self.kongfu = "现代煎饼果子配方"
#
#     def make_cake(self):
#         print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#
# class Prentice(School, Master):  # 多继承，继承了多个父类
#     def __init__(self):
#         self.kongfu = "猫氏煎饼果子配方"
#
#     def make_cake(self):
#         print("执行子类的__init__方法前，self.kongfu属性：%s" % self.kongfu)
#         self.__init__() # 执行本类的__init__方法，做属性初始化 self.kongfu = "猫氏...."
#         print("执行子类的__init__方法前，self.kongfu属性：%s" % self.kongfu)
#         print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#
#     # 调用父类方法格式：父类类名.父类方法(self)
#     def make_old_cake(self):
#         # 不推荐这样访问父类的实例属性，相当于创建了一个新的父类对象
#         # print("直接调用Master类的kongfu属性：%s" % Master().kongfu)
#
#
#         # 可以通过执行Master类的__init__方法，来修改self的属性值
#         print("执行Master类的__init__方法前，self.kongfu属性：%s" % self.kongfu)
#         Master.__init__(self) # 调用了父类Master的__init__方法 self.kongfu = "古法...."
#         print("执行Master类的__init__方法后，self.kongfu属性：%s" % self.kongfu)
#         Master.make_cake(self) # 调用父类Master的实例方法
#
#
#     def make_new_cake(self):
#         # 不推荐这样访问类的实例属性，相当于创建了一个新的父类对象
#         # print("直接调用School类的kongfu属性：%s" % School().kongfu)
#
#         # 可以通过执行School类的__init__方法，来修改self的属性值
#         print("执行School类的__init__方法前，self.kongfu属性：%s" % self.kongfu)
#         School.__init__(self) # 调用了父类School的__init__方法 self.kongfu = "现代...."
#         print("执行School类的__init__方法后，self.kongfu属性：%s" % self.kongfu)
#         School.make_cake(self) # 调用父类School的实例方法
#
#     def make_new_cake(self):
#         # 不推荐这样访问类的实例属性，相当于创建了一个新的父类对象
#         # print("直接调用School类的kongfu属性：%s" % School().kongfu)
#
#         # 可以通过执行School类的__init__方法，来修改self的属性值
#         print("执行School类的__init__方法前，self.kongfu属性：%s" % self.kongfu)
#         School.__init__(self)  # 调用了父类School的__init__方法 self.kongfu = "现代...."
#         print("执行School类的__init__方法后，self.kongfu属性：%s" % self.kongfu)
#         School.make_cake(self)  # 调用父类School的实例方法
#
# # 实例化对象，自动执行子类的__init__方法
# damao = Prentice()
#
# damao.make_cake()  # 调用子类的方法（默认重写了父类的同名方法）
#
# print("--" * 10)
# damao.make_old_cake()  # 进入实例方法去调用父类Master的方法
#
# print("--" * 10)
# damao.make_new_cake()  # 进入实例方法去调用父类School的方法
#
# print("--" * 10)
# damao.make_cake()  # 调用本类的实例方法


# 大猫的煎饼果子店非常红火，终于有一天，他成了世界首富！！
# 但是他也老了，所以他希望把 师傅的配方 和 学校的配方 以及自己的配方 继续传承下去...（多层继承）

# class Master(object):
#     def __init__(self):
#         self.kongfu = "古法煎饼果子配方"
#     def make_cake(self):
#         print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
# class School(object):
#     def __init__(self):
#         self.kongfu = "现代煎饼果子配方"
#     def make_cake(self):
#         print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
# class Prentice(Master,School):
#     def __init__(self):
#         self.kongfu = "猫氏煎饼果子配方"
#         self.money = 10000  # 亿美金
#     def make_cake(self):
#         self.__init__()  # 执行本类的__init__方法，做属性初始化 self.kongfu = "猫氏...."
#         print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#         # 调用父类方法格式：父类类名.父类方法(self)
#
#     def make_old_cake(self):
#         Master.__init__(self)  # 调用了父类Master的__init__方法 self.kongfu = "古法...."
#         Master.make_cake(self)  # 调用了父类Master的实例方法
#
#     def make_new_cake(self):
#         School.__init__(self)  # 调用了父类School的__init__方法 self.kongfu = "现代...."
#         School.make_cake(self)  # 调用父类School的实例方法，
#
#
# class PrenticePrentice(Prentice): # 多层继承
#     pass
#
# pp = PrenticePrentice()
# pp.make_cake()
# pp.make_new_cake()
# pp.make_old_cake()
# print(pp.money)



# super()的使用

# class Master(object):
#     def __init__(self):
#         self.kongfu = "古法煎饼果子配方"  # 实例变量，属性
#
#     def make_cake(self):  # 实例方法，方法
#         print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#
# # 父类是 Master类
# class School(Master):
#     def __init__(self):
#         self.kongfu = "现代煎饼果子配方"
#
#     def make_cake(self):
#         print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#         super().__init__()  # 执行父类的构造方法
#         super().make_cake()  # 执行父类的实例方法
#
#
# # 父类是 School 和 Master
# class Prentice(School, Master):  # 多继承，继承了多个父类
#     def __init__(self):
#         self.kongfu = "猫氏煎饼果子配方"
#
#     def make_cake(self):
#         self.__init__()  # 执行本类的__init__方法，做属性初始化 self.kongfu = "猫氏...."
#         print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#     def make_all_cake(self):
#         # 方式1. 指定执行父类的方法（代码臃肿）
#         # School.__init__(self)
#         # School.make_cake(self)
#         #
#         # Master.__init__(self)
#         # Master.make_cake(self)
#         #
#         # self.__init__()
#         # self.make_cake()
#
#         # 方法2. super() 带参数版本，只支持新式类
#         # super(Prentice, self).__init__() # 执行父类的 __init__方法
#         # super(Prentice, self).make_cake()
#         # self.make_cake()
#
#         # 方法3. super()的简化版，只支持新式类
#         super().__init__()  # 执行父类的 __init__方法
#         super().make_cake()  # 执行父类的 实例方法
#         self.make_cake()  # 执行本类的实例方法
#
#
# damao = Prentice()
# damao.make_cake()
# damao.make_all_cake()

# print(Prentice.__mro__)
# 知识点：
# 子类继承了多个父类，如果父类类名修改了，那么子类也要涉及多次修改。而且需要重复写多次调用，显得代码臃肿。
#
# 使用super() 可以逐一调用所有的父类方法，并且只执行一次。调用顺序遵循 mro 类属性的顺序。
#
# 注意：如果继承了多个父类，且父类都有同名方法，则默认只执行第一个父类的(同名方法只执行一次，目前super()不支持执行多个父类的同名方法)
#
# super() 在Python2.3之后才有的机制，用于通常单继承的多层继承。

# 大猫觉得配方传承下去没问题，但是钱是辛辛苦苦挣得血汗钱，不想传给徒弟。（私有权限）



'''私有权限
面向对象三大特性：封装、继承、多态
封装的意义：
1、将属性和方法放到一起做为一个整体，然后通过实例化对象来处理；
2、隐藏内部实现细节，只需要和对象及其属性和方法交互就可以了；
3、对类的属性和方法增加 访问权限控制。
私有权限：在属性名和方法名 前面 加上两个下划线 __
1、类的私有属性 和 私有方法，都不能通过对象直接访问，但是可以在本类内部访问；
2、类的私有属性 和 私有方法，都不会被子类继承，子类也无法访问；
3、私有属性 和 私有方法 往往用来处理类的内部事情，不通过对象处理，起到安全作用。'''

# class Master(object):
#     def __init__(self):
#         self.kongfu = "古法煎饼果子配方"
#     def make_cake(self):
#         print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
# class School(object):
#     def __init__(self):
#         self.kongfu = "现代煎饼果子配方"
#
#     def make_cake(self):
#         print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
# class Prentice(School, Master):
#     def __init__(self):
#         self.kongfu = "猫氏煎饼果子配方"
#         # 私有属性，可以在类内部通过self调用，但不能通过对象访问
#         self.__money = 10000
#
#     # 私有方法，可以在类内部通过self调用，但不能通过对象访问
#     def __print_info(self):
#         print(self.kongfu)
#         print(self.__money)
#
#     def make_cake(self):
#         self.__init__()
#         print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#     def make_old_cake(self):
#         Master.__init__(self)
#         Master.make_cake(self)
#
#
#     def make_new_cake(self):
#         School.__init__(self)
#         School.make_cake(self)
#
# class PrenticePrentice(Prentice):
#     pass
#
#
# damao = Prentice()
# # 对象不能访问私有权限的属性和方法
# print(damao.__money)
# damao.__print_info()
#
#
# pp = PrenticePrentice()
# # 子类不能继承父类私有权限的属性和方法
# print(pp.__money)
# pp.__print_info()

# 总结
# Python中没有像C++中 public 和 private 这些关键字来区别公有属性和私有属性。
# Python是以属性命名方式来区分，如果在属性和方法名前面加了2个下划线'__'，则表明该属性和方法是私有权限，否则为公有权限。


'''修改私有属性的值
如果需要修改一个对象的属性值，通常有2种方法

对象名.属性名 = 数据 ----> 直接修改
对象名.方法名() ----> 间接修改
私有属性不能直接访问，所以无法通过第一种方式修改，一般的通过第二种方式修改私有属性的值：
定义一个可以调用的公有方法，在这个公有方法内访问修改。'''


# class Master(object):
#     def __init__(self):
#         self.kongfu = "古法煎饼果子配方"
#     def make_cake(self):
#         print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
# class School(object):
#     def __init__(self):
#         self.kongfu = "现代煎饼果子配方"
#
#     def make_cake(self):
#         print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
# class Prentice(School, Master):
#     def __init__(self):
#         self.kongfu = "猫氏煎饼果子配方"
#         # 私有属性，可以在类内部通过self调用，但不能通过对象访问
#         self.__money = 10000
#
#
#     # 现代软件开发中，通常会定义get_xxx()方法和set_xxx()方法来获取和修改私有属性值。
#
#     # 返回私有属性的值
#     def get_money(self):
#         return self.__money
#
#     # 接收参数，修改私有属性的值
#     def set_money(self, num):
#         self.__money = num
#
#
#     def make_cake(self):
#         self.__init__()
#         print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
#
#     def make_old_cake(self):
#         Master.__init__(self)
#         Master.make_cake(self)
#
#     def make_new_cake(self):
#         School.__init__(self)
#         School.make_cake(self)
#
# class PrenticePrentice(Prentice):
#     pass
#
#
# damao = Prentice()
# # 对象不能访问私有权限的属性和方法
# # print(damao.__money)
# # damao.__print_info()
#
# # 可以通过访问公有方法set_money()来修改私有属性的值
# damao.set_money(100)
#
# # 可以通过访问公有方法get_money()来获取私有属性的值
# print(damao.get_money())

'''多态
所谓多态：定义时的类型和运行时的类型不一样，此时就成为多态 ，多态的概念是应用于Java和C
#这一类强类型语言中，而Python崇尚“鸭子类型”。

鸭子类型：虽然我想要一只"鸭子"，但是你给了我一只鸟。 
但是只要这只鸟走路像鸭子，叫起来像鸭子，游泳也像鸭子，
我就认为这是鸭子。

Python的多态，就是弱化类型，重点在于对象参数是否有指定的属性和方法，
如果有就认定合适，而不关心对象的类型是否正确。

Python伪代码实现Java或C#的多态'''

# class F1(object):
#     def show(self):
#         print('F1.show')
#
# class S1(F1):
#     def show(self):
#         print('S1.show')
#
# class S2(F1):
#     def show(self):
#         print('S2.show')
#
# # 由于在Java或C#中定义函数参数时，必须指定参数的类型
# # 为了让Func函数既可以执行S1对象的show方法，又可以执行S2对象的show方法，
# # 所以在def Func的形参中obj的类型是 S1和S2的父类即F1
# #
# # 而实际传入的参数是：S1对象和S2对象
#
# def Func(F1 obj):
#
#     # Func函数需要接收一个F1类型或者F1子类的类型
#
#     print(obj.show())
#
# def Func(F1 obj):
#     print(obj.)
#
# s1_obj = S1()
# Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj，执行 S1 的show方法，结果：S1.show
#
# s2_obj = S2()
# Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj，执行 Ss 的show方法，结果：S2.show
# '''通俗点理解：定义obj这个变量是说的类型是：F1的类型，
# 但是在真正调用Func函数时给其传递的不一定是F1类的实例对象，
# 有可能是其子类的实例对象， 这种情况就是所谓的多态'''
#
# # Python “鸭子类型”
# class F1(object):
#     def show(self):
#         print('F1.show')
#
# class S1(F1):
#     def show(self):
#         print('S1.show')
#
# class S2(F1):
#     def show(self):
#         print('S2.show')
#
# def Func(obj):
#     # python是弱类型，即无论传递过来的是什么，obj变量都能够指向它，这也就没有所谓的多态了（弱化了这个概念）
#     print(obj.show())
#
# s1_obj = S1()
# Func(s1_obj)
#
# s2_obj = S2()
# Func(s2_obj)

'''类属性和实例属性
在了解了类基本的东西之后，下面看一下python中这几个概念的区别

先来谈一下类属性和实例属性

在前面的例子中我们接触到的就是实例属性（对象属性），顾名思义，
类属性就是类对象所拥有的属性，它被所有类对象的实例对象所共有，
在内存中只存在一个副本，这个和C++中类的静态成员变量有点类似。
对于公有的类属性，在类外可以通过类对象和实例对象访问

类属性'''
class People(object):
    name = 'Tom'  # 公有的类属性
    __age = 12  # 私有的类属性

p = People()

print(p.name)  # 正确
print(People.name)  # 正确
print(p.__age)  # 错误，不能在类外通过实例对象访问私有的类属性
print(People.__age) # 错误，不能在类外通过类对象访问私有的类属性
# 实例属性(对象属性)
class People(object):
    address = '山东'  # 类属性
    def __init__(self):
        self.name = 'xiaowang'  # 实例属性
        self.age = 20  # 实例属性

p = People()
p.age = 12  # 实例属性
print(p.address)  # 正确
print(p.name)  # 正确
print(p.age)  # 正确

print(People.address)  # 正确
print(People.name)  # 错误
print(People.age)  # 错误
# 通过实例(对象)去修改类属性
class People(object):
    country = 'china' #类属性


print(People.country)
p = People()
print(p.country)
p.country = 'japan'
print(p.country)  # 实例属性会屏蔽掉同名的类属性
print(People.country)
del p.country  # 删除实例属性
print(p.country)
'''总结
如果需要在类外修改类属性，必须通过类对象去引用然后进行修改。
如果通过实例对象去引用，会产生一个同名的实例属性，这种方式修改的是实例属性，
不会影响到类属性，并且之后如果通过实例对象去引用该名称的属性，
实例属性会强制屏蔽掉类属性，即引用的是实例属性，除非删除了该实例属性。'''


'''静态方法和类方法
1. 类方法
是类对象所拥有的方法，需要用修饰器@classmethod来标识其为类方法，
对于类方法，第一个参数必须是类对象，一般以cls作为第一个参数（当然可以用其他名称的变量作为其第一个参数，
但是大部分人都习惯以'cls'作为第一个参数的名字，就最好用'cls'了），能够通过实例对象和类对象去访问。'''

class People(object):
    # 私有类属性
    __country = 'china'

    #类方法，用classmethod来进行修饰
    @classmethod
    def get_country(cls):
        return cls.__country

p = People()
print(p.get_country())    #可以用过实例对象引用
print(People.get_country())    #可以通过类对象引用
'''类方法还有一个用途就是可以对类属性进行修改：'''

class People(object):
    # 私有类属性
    __country = 'china'

    #类方法，用classmethod来进行修饰
    @classmethod
    def get_country(cls):
        return cls.__country

    @classmethod
    def set_country(cls,country):
        cls.__country = country


p = People()
print(p.get_country())   #可以用过实例对象访问
print(People.get_country())    #可以通过类访问

p.set_country('japan')

print(p.get_country())
print(People.get_country())
'''结果显示在用类方法对类属性修改之后，通过类对象和实例对象访问都发生了改变'''

'''2. 静态方法
需要通过修饰器@staticmethod来进行修饰，静态方法不需要多定义参数，可以通过对象和类来访问。'''

class People(object):
    country = 'china'

    @staticmethod
    #静态方法
    def get_country():
        return People.country


p = People()
# 通过对象访问静态方法
p.get_contry()

# 通过类访问静态方法
print(People.get_country())
'''总结
从类方法和实例方法以及静态方法的定义形式就可以看出来，类方法的第一个参数是类对象cls，那么通过cls引用的必定是类对象的属性和方法；
实例方法的第一个参数是实例对象self，那么通过self引用的可能是类属性、
也有可能是实例属性（这个需要具体分析），不过在存在相同名称的类属性和实例属性的情况下，实例属性优先级更高。
静态方法中不需要额外定义参数，因此在静态方法中引用类属性的话，必须通过类实例对象来引用'''